package com.terra.ns.imp.system.service.impl

import cn.hutool.core.bean.BeanUtil
import cn.hutool.core.collection.CollUtil
import cn.hutool.extra.spring.SpringUtil
import com.alibaba.fastjson2.JSONObject
import com.terra.ns.imp.common.dingtalk.contact.domain.UserV2DTO
import com.terra.ns.imp.common.dingtalk.contact.service.ContactService
import com.terra.ns.imp.system.api.entity.StUser
import com.terra.ns.imp.system.config.SystemCustomProperties
import com.terra.ns.imp.system.constant.SystemCacheKeys
import com.terra.ns.imp.system.constant.SystemConstants
import com.terra.ns.imp.system.constant.SystemErrorCode
import com.terra.ns.imp.system.constant.enums.DeptSyncTypeEnum
import com.terra.ns.imp.system.constant.enums.UserEventEnum
import com.terra.ns.imp.system.convert.DeptDingExtConvert
import com.terra.ns.imp.system.convert.UserConvert
import com.terra.ns.imp.system.convert.UserDeptRelConvert
import com.terra.ns.imp.system.convert.UserDingExtConvert
import com.terra.ns.imp.system.entity.StDepartment
import com.terra.ns.imp.system.entity.StDeptDingExt
import com.terra.ns.imp.system.entity.StUserDingExt
import com.terra.ns.imp.system.event.UserEvent
import com.terra.ns.imp.system.exception.SystemServiceException
import com.terra.ns.imp.system.mapper.*
import com.terra.ns.imp.system.service.IDeptSyncService
import com.terra.ns.imp.system.service.IStUserService
import com.terra.ns.imp.system.service.helper.StDepartmentHelper
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import kotlin.math.roundToInt

/**
@author qins
@date 2023/6/16
@desc 系统与钉钉组织架构部门关系操作
 */
@Service("dingDeptSyncService")
class DingDeptSyncServiceImpl : IDeptSyncService {

    private val log = LoggerFactory.getLogger(this::class.java)

    @Autowired
    private lateinit var userService: IStUserService

    @Autowired
    private lateinit var contactService: ContactService

    @Autowired
    private lateinit var systemCustomProperties: SystemCustomProperties

    @Autowired
    private lateinit var departmentHelper: StDepartmentHelper

    @Autowired
    private lateinit var departmentMapper: StDepartmentMapper

    @Autowired
    private lateinit var deptDingExtMapper: StDeptDingExtMapper

    @Autowired
    private lateinit var userDeptRelMapper: StUserDeptRelMapper

    @Autowired
    private lateinit var userDingExtMapper: StUserDingExtMapper

    @Autowired
    private lateinit var userRoleRelMapper: StUserRoleRelMapper

    @Autowired
    private lateinit var userMapper: StUserMapper


    @Transactional
    override fun doSyncDept(syncType: DeptSyncTypeEnum) {
        log.info("开始同步钉钉组织架构")
        var syncRate = 0 // 同步进度
        try {
            val dingDeptList = contactService.getDepartmentList("")
            if (dingDeptList.isNotEmpty()) {
                // 更新总部门数量
                val deptTotal = dingDeptList.size
                updateSyncDeptCacheItem(SystemCacheKeys.IMPORT_DEPT_TOTAL_KEY, deptTotal)
                val recurDeptList = CollUtil.newArrayList(dingDeptList)
                var completeNum = 0
                while (recurDeptList.isNotEmpty()) {
                    for (idx in recurDeptList.count() - 1 downTo 0) {
                        val item = recurDeptList[idx]
                        if (doSyncDingSingleDept(item.id,false)) {
                            // 更新成功部门数量
                            completeNum++
                            recurDeptList.remove(item)
                            updateSyncDeptCacheItem(SystemCacheKeys.IMPORT_DEPT_SUCCESS_COUNT_KEY, completeNum)
                        }
                        // 计算同步进度，保留整数
                        syncRate = ((completeNum.toDouble() / deptTotal) * 100).roundToInt()
                        updateSyncDeptCacheItem(SystemCacheKeys.IMPORT_DEPT_RATE_KEY, syncRate)
                        log.info("当前同步第{}个部门，此批部门数：{}，同步总进度：{}%", idx + 1, recurDeptList.size, syncRate)
                    }
                }
                if (syncRate == 100) {
                    clearCompanyNotBindDingDept(syncType)
                    clearCompanyNoRelUser(syncType)
                }
            }
            updateSyncDeptCacheItems(
                mapOf(Pair(SystemCacheKeys.IMPORT_DEPT_RATE_KEY, 100),
                    Pair(SystemCacheKeys.IMPORT_DEPT_RESULT_DESC_KEY, "同步成功")))
            log.info("结束同步钉钉组织架构，总部门数：{}", dingDeptList.size)
        } catch (e: Exception) {
            log.error("同步钉钉组织架构失败，进度： {}%", syncRate)
            updateSyncDeptCacheItems(
                mapOf(Pair(SystemCacheKeys.IMPORT_DEPT_RATE_KEY, -1),
                    Pair(SystemCacheKeys.IMPORT_DEPT_RESULT_DESC_KEY, e.message ?: SystemErrorCode.SYNC_DING_DEPT_ERROR.code)))
            e.printStackTrace()
            throw SystemServiceException(SystemErrorCode.SYNC_DING_DEPT_ERROR)
        }
    }

    @Transactional
    @Synchronized
    override fun addUserEventHandle(eventJson: JSONObject) {
        val userIds = eventJson.getJSONArray("userId").toJavaList(String::class.java)
        log.info("开始处理钉钉新增用户事件，用户ID: {}", userIds.toString())
        if(userIds.isEmpty()) return
        val existUserIdSet = userDingExtMapper.selectListByUserIds(userIds).mapNotNull { it.userid }.toSet()
        val newUserIds = userIds.subtract(existUserIdSet)
        newUserIds.forEach {
            doSyncOneUserByDingUserId(it)
        }
        log.info("处理钉钉新增用户事件结束，实际新增ID: {}", newUserIds.toString())
    }

    @Transactional
    @Synchronized
    override fun editUserEventHandle(eventJson: JSONObject) {
        val userIds = eventJson.getJSONArray("userId").toJavaList(String::class.java)
        log.info("开始处理钉钉修改用户事件，用户ID: {}", userIds.toString())
        if(userIds.isEmpty()) return
        val existUserIdSet = userDingExtMapper.selectListByUserIds(userIds).mapNotNull { it.userid }.toSet()
        val realEditUserIds = userIds.intersect(existUserIdSet)
        realEditUserIds.forEach {
            doSyncEditUserByDingUserId(it)
        }
        log.info("处理钉钉编辑用户事件结束，实际变更ID: {}", realEditUserIds.toString())
    }

    @Transactional
    @Synchronized
    override fun exitUserEventHandle(eventJson: JSONObject) {
        val userIds = eventJson.getJSONArray("userId").toJavaList(String::class.java)
        log.info("开始处理钉钉用户离职事件，用户ID: {}", userIds.toString())
        if(userIds.isEmpty()) return
        val existUserIdSet = userDingExtMapper.selectListByUserIds(userIds).mapNotNull { it.userid }.toSet()
        val realExitUserIds = userIds.intersect(existUserIdSet)
        realExitUserIds.forEach {
            doSyncExitUserByDingUserId(it)
        }
        log.info("处理钉钉用户离职事件结束，实际变更ID: {}", realExitUserIds.toString())
    }

    @Transactional
    @Synchronized
    override fun addDeptEventHandle(eventJson: JSONObject) {
        val deptIds = eventJson.getJSONArray("deptId").toJavaList(Long::class.java)
        log.info("开始处理钉钉添加部门事件，部门ID: {}", deptIds.toString())
        if(deptIds.isEmpty()) return
        val existDeptIdSet = deptDingExtMapper.selectOneByDingDeptIds(deptIds).mapNotNull { it.deptId }.toSet()
        val realAddDeptIds = deptIds.subtract(existDeptIdSet)
        if(realAddDeptIds.isEmpty()) return
        realAddDeptIds.forEach {
            doSyncDingSingleDept(it, true)
        }
        log.info("处理钉钉添加部门事件结束，实际新增部门ID: {}", realAddDeptIds.toString())
    }

    @Transactional
    @Synchronized
    override fun editDeptEventHandle(eventJson: JSONObject) {
        val deptIds = eventJson.getJSONArray("deptId").toJavaList(Long::class.java)
        log.info("开始处理钉钉更新部门事件，部门ID: {}", deptIds.toString())
        if(deptIds.isEmpty()) return
        val existDeptIdSet = deptDingExtMapper.selectOneByDingDeptIds(deptIds).mapNotNull { it.deptId }.toSet()
        val realEditDeptIds = deptIds.intersect(existDeptIdSet)
        if(realEditDeptIds.isEmpty()) return
        realEditDeptIds.forEach {
            doSyncDingSingleDept(it, false)
        }
        log.info("处理钉钉更新部门事件结束，实际更新部门ID: {}", realEditDeptIds.toString())
    }

    @Transactional
    @Synchronized
    override fun deleteDeptEventHandle(eventJson: JSONObject) {
        val deptIds = eventJson.getJSONArray("deptId").toJavaList(Long::class.java)
        log.info("开始处理钉钉删除部门事件，部门ID: {}", deptIds)
        if(deptIds.isEmpty()) return
        val existDeptIdSet = deptDingExtMapper.selectOneByDingDeptIds(deptIds).mapNotNull { it.deptId }.toSet()
        val realDelDeptIds = deptIds.intersect(existDeptIdSet)
        if(realDelDeptIds.isEmpty()) return
        realDelDeptIds.forEach {
            doDeleteDeptByDingDeptId(it)
        }
        log.info("处理钉钉沙漠部门事件结束，实际删除部门ID: {}", realDelDeptIds.toString())
    }


    /**
     * 同步单个部门信息
     */
    fun doSyncDingSingleDept(dingDeptId: Long, syncChildDept: Boolean): Boolean {
        log.info("开始同步部门 {} 数据", dingDeptId)
        val deptDetail = contactService.departmentDetail(dingDeptId.toString())
        //判断父节点是否存在，若不存在则将本节点放置列表最后，跳出本次操作
        if(deptDetail.id != SystemConstants.DING_TOP_DEPT_ID
            && deptDingExtMapper.selectOneByDingDeptId(deptDetail.parentid) == null) {
            return false
        }
        val newDingExt = DeptDingExtConvert.convert(deptDetail)
        val existDingExt = deptDingExtMapper.selectOneByDingDeptId(deptDetail.id)
        if(existDingExt == null) {
            // 新增系统部门，填充相关字段
            val newStDept = StDepartment()
            newStDept.name = deptDetail.name
            newStDept.code = departmentHelper.generateDeptCode()
            newStDept.parentCode = deptDingExtMapper.selectOneByDingDeptId(newDingExt.parentId!!)!!.sourceIdentifier
            newStDept.nodePath = departmentHelper.computeNodePath(newStDept.parentCode, newStDept.code!!)
            newStDept.topNodeCode = departmentHelper.computeTopCode(newStDept.parentCode)
            departmentMapper.insert(newStDept)
            newDingExt.sourceIdentifier = newStDept.code
            deptDingExtMapper.insert(newDingExt)
            incrementSyncDeptCacheItem(SystemCacheKeys.IMPORT_DEPT_NEW_DEPT_COUNT_KEY)
        } else {
            // 更新部门信息
            val update = StDepartment()
            update.name = newDingExt.name
            departmentMapper.updateByCode(update, existDingExt.sourceIdentifier ?: "")

            newDingExt.sourceIdentifier = update.code
            newDingExt.parentId = existDingExt.parentId
            deptDingExtMapper.updateAllFieldByDingDeptId(newDingExt, dingDeptId, listOf(StDeptDingExt::sourceIdentifier.name))
        }
        doSyncDingSingleDeptUser(dingDeptId)
        if(syncChildDept) {  //若需要同步子部门,查询此部门的子部门，依次递归此同步方法
            val dingDeptList = contactService.getDepartmentList(dingDeptId.toString())
            dingDeptList.forEach {
                doSyncDingSingleDept(it.id, true)
            }
        }
        return true
    }

    /**
     * 同步单个部门下的用户
     */
    fun doSyncDingSingleDeptUser(dingDeptId: Long) {
        var offset = 0L
        val size = 50L
        val userDtoV2List = ArrayList<UserV2DTO>()
        var currentList: List<UserV2DTO>
        //循环分页取出用户列表数据
        do {
            currentList = contactService.getUserList(dingDeptId, offset, size).filterNot { it.mobile.isNullOrBlank() }
            if (currentList.isNotEmpty()) {
                userDtoV2List.addAll(currentList)
            }
            offset += size
        } while (currentList.isNotEmpty() && currentList.size.toLong() == size)
        //部门主管数据预备
        val leader = StUserDingExt()
        //部门数据预备
        val deptDingExt = deptDingExtMapper.selectOneByDingDeptId(dingDeptId)!!
        val deptCode = deptDingExt.sourceIdentifier!!
        val deptRelUserCodeList = mutableListOf<String>()
        if (userDtoV2List.isNotEmpty()) {
            userDtoV2List.stream().forEach {
                deptRelUserCodeList.add(this.doSyncOneDingUserByDept(it, dingDeptId, deptCode, leader))
            }
            //部门主管数据关联更新
            if (leader.mobile != null) {
                //取系统中部门信息
                val existStDept = departmentMapper.selectOneByCode(deptCode)!!
                val updateDept = StDepartment()
                //取系统中leader用户信息
                val stUser = userMapper.selectOneByPhone(leader.mobile!!)!!
                //将用户CODE赋予部门主管code
                updateDept.principalCode = stUser.code
                updateDept.id = existStDept.id
                departmentMapper.updateById(updateDept)
            }
        }
        //查询此时数据库中存在部门关系的用户数据
        val deptRelExistUserCodeList =  userDeptRelMapper.selectListByDeptCode(deptCode).mapNotNull { it.userCode }
        //不在此次名单中的成员部门关系移除
        val delDeptRelUserCodeList = deptRelExistUserCodeList.subtract(deptRelUserCodeList.toSet())
        if (delDeptRelUserCodeList.isNotEmpty()) {
            userDeptRelMapper.deleteByUserCodesAndDeptCode(
                delDeptRelUserCodeList.toList(),
                deptCode,
            )
        }

    }

    fun doSyncOneDingUserByDept(
        dingUser: UserV2DTO,
        dingDeptId: Long,
        stDeptCode: String,
        leader: StUserDingExt
    ): String {
        // 查询库内的手机号
        val existStUser = userMapper.selectOneByPhone(dingUser.mobile)
        var userCode = existStUser?.code
        val stUser = UserConvert.convert(dingUser)
        if (existStUser == null) {
            //新建一个用户
            stUser.password = userService.encodePassword(null)
            userCode = stUser.code
            userMapper.insert(stUser)
            //缓存中新增用户数+1
            incrementSyncDeptCacheItem(SystemCacheKeys.IMPORT_DEPT_NEW_USER_COUNT_KEY)
        } else {
            userMapper.updateAllFieldByUserCode(stUser, userCode!!, listOf(StUser::code.name, StUser::password.name, StUser::status.name, StUser::wxUnionId.name))
            //缓存中覆盖用户数+1
            incrementSyncDeptCacheItem(SystemCacheKeys.IMPORT_DEPT_COVER_USER_COUNT_KEY)
        }
        userCode!!
        //查询用户在该企业下的钉钉信息
        val existUserDingExt = userDingExtMapper.selectOneByUserId(dingUser.userid)
        if(existUserDingExt == null) {
            val newDingUserExt = UserDingExtConvert.convert(dingUser)
            newDingUserExt.userCode = userCode
            userDingExtMapper.insert(newDingUserExt)
        } else {
            val updateDingUserExt = UserDingExtConvert.convert(dingUser)
            updateDingUserExt.userCode = userCode
            userDingExtMapper.updateAllFieldById(updateDingUserExt, existUserDingExt.id!!)
        }
        //查询部门与用户的关系
        val userDeptRel = userDeptRelMapper.selectOneByUserCodeAndDeptCode(userCode, stDeptCode)
        if (userDeptRel == null) {
            userDeptRelMapper.insert(UserDeptRelConvert.convert(userCode, stDeptCode, ""))
        }
        if (dingUser.leader) {
            BeanUtil.copyProperties(dingUser, leader)
        }
        return userCode
    }

    /**
     * 根据同步策略清除不是钉钉中的部门
     */
    fun clearCompanyNotBindDingDept(syncTypeEnum: DeptSyncTypeEnum) {
        //同步完部门信息之后针对不同同步策略做出处理
        when (syncTypeEnum) {
            //已有该部门时，按照不同同步策略分别处理
            DeptSyncTypeEnum.FULL -> {
                //查询企业名下的部门清单
                val allDeptCode = departmentMapper.selectAllDept().mapNotNull { it.code }
                //查询钉钉部门信息列表
                val dingDeptExtCodeList = deptDingExtMapper.selectListBySourceIdentifierList(allDeptCode).mapNotNull { it.sourceIdentifier }
                val delDeptCodeList = allDeptCode.filterNot { dingDeptExtCodeList.contains(it) }
                if (delDeptCodeList.isNotEmpty()) {
                    //删除部门
                    departmentMapper.deleteByCodes(delDeptCodeList)
                    //删除部门名下的部门与用户关系数据
                    userDeptRelMapper.deleteByDeptCodes(delDeptCodeList)
                }
            }
            //默认增量更新不做处理
            DeptSyncTypeEnum.INCREMENT -> {}
        }
    }

    /**
     * 清除没有关联的用户
     */
    fun clearCompanyNoRelUser(syncTypeEnum: DeptSyncTypeEnum) {
        //清除企业名下无部门关系的用户
        clearCompanyNoDeptUser()
        //同步完部门信息之后针对不同同步策略做出处理
        when (syncTypeEnum) {
            //按照不同同步策略分别处理
            DeptSyncTypeEnum.FULL -> {
                //全量时，将未关联的用户删除
                clearCompanyNotBindDingUser()
            }
            //默认增量更新不做处理
            DeptSyncTypeEnum.INCREMENT -> {}
        }
    }

    /**
     * 清除没有部门的用户
     */
    fun clearCompanyNoDeptUser() {
        val userCodeList = userMapper.selectAllUser().mapNotNull { it.code }
        val hasDeptUserCodeList = userDeptRelMapper.selectListByUserCodes(userCodeList).mapNotNull { it.userCode }
        val noDeptUserCodeList = userCodeList.filterNot { hasDeptUserCodeList.contains(it) }
        if(noDeptUserCodeList.isNotEmpty()) {
            userDeptRelMapper.deleteByUserCodes(noDeptUserCodeList)
            userRoleRelMapper.deleteByUserCodes(noDeptUserCodeList)
        }
        SpringUtil.publishEvent(UserEvent(noDeptUserCodeList, UserEventEnum.DELETE))
    }

    /**
     * 清除未关联钉钉用户的用户
     */
    fun clearCompanyNotBindDingUser() {
        val userCodeList = userMapper.selectAllUser().mapNotNull { it.code }
        val hasDingExtRelCodeList = userDingExtMapper.selectListByRelCodes(userCodeList).mapNotNull { it.userCode }
        val noDingExtRelCodeList = userCodeList.filterNot { hasDingExtRelCodeList.contains(it) }
        val delUserCodeList = userCodeList.filter { noDingExtRelCodeList.contains(it) }
        if(delUserCodeList.isNotEmpty()) {
            userDeptRelMapper.deleteByUserCodes(delUserCodeList)
            userRoleRelMapper.deleteByUserCodes(delUserCodeList)
        }
        SpringUtil.publishEvent(UserEvent(delUserCodeList, UserEventEnum.DELETE))
    }

    /**
     * 根据钉钉用户ID同步一个用户信息
     */
    fun doSyncOneUserByDingUserId(dingUserId: String) {
        val dingUser = contactService.getUserDetail(dingUserId)
        // 查询库内的手机号
        val existStUser = userMapper.selectOneByPhone(dingUser.mobile)
        var userCode = existStUser?.code
        val stUser = UserConvert.convert(dingUser)
        if (existStUser == null) {
            //新建一个用户
            stUser.password = userService.encodePassword(null)
            userCode = stUser.code
            userMapper.insert(stUser)
        } else {
            userMapper.updateAllFieldByUserCode(stUser, userCode!!, listOf(StUser::code.name, StUser::password.name, StUser::status.name, StUser::wxUnionId.name))
        }
        userCode!!
        //查询用户在该企业下的钉钉信息
        val existUserDingExt = userDingExtMapper.selectOneByUserId(dingUser.userid)
        if(existUserDingExt == null) {
            val newDingUserExt = UserDingExtConvert.convert(dingUser)
            newDingUserExt.userCode = userCode
            userDingExtMapper.insert(newDingUserExt)
        } else {
            val updateDingUserExt = UserDingExtConvert.convert(dingUser)
            updateDingUserExt.userCode = userCode
            userDingExtMapper.updateAllFieldById(updateDingUserExt, existUserDingExt.id!!)
        }
        // 部门关系
        dingUser.leaderInDept.forEach {
            val dingDeptExt = deptDingExtMapper.selectOneByDingDeptId(it.deptId)
            var stDeptCode = dingDeptExt?.sourceIdentifier
            if(dingDeptExt == null) {
                // 新增部门
                val dingDept = contactService.departmentDetail(it.deptId.toString())
                if(dingDept.id != SystemConstants.DING_TOP_DEPT_ID
                    && deptDingExtMapper.selectOneByDingDeptId(dingDept.parentid) == null) {
                    return@forEach
                }
                val newDingExt = DeptDingExtConvert.convert(dingDept)
                // 新增系统部门，填充相关字段
                val newStDept = StDepartment()
                newStDept.name = dingDept.name
                newStDept.code = departmentHelper.generateDeptCode()
                newStDept.parentCode = deptDingExtMapper.selectOneByDingDeptId(newDingExt.parentId!!)!!.sourceIdentifier
                newStDept.nodePath = departmentHelper.computeNodePath(newStDept.parentCode, newStDept.code!!)
                newStDept.topNodeCode = departmentHelper.computeTopCode(newStDept.parentCode)
                if(it.leader) {
                    newStDept.principalCode = userCode
                }
                departmentMapper.insert(newStDept)
                newDingExt.sourceIdentifier = newStDept.code
                stDeptCode = newStDept.code
                deptDingExtMapper.insert(newDingExt)
            } else {
                // 更新部门责任人信息
                if(it.leader) {
                    val update = StDepartment()
                    update.principalCode = userCode
                    departmentMapper.updateByCode(update, stDeptCode ?: "")
                }
           }
            // 新增部门关系
            userDeptRelMapper.selectOneByUserCodeAndDeptCode(userCode, stDeptCode!!)
                ?: userDeptRelMapper.insert( UserDeptRelConvert.convert(userCode, stDeptCode, null))
        }
    }

    /**
     * 根据钉钉用户ID更新一个用户信息
     */
    fun doSyncEditUserByDingUserId(dingUserId: String) {
        val dingUser = contactService.getUserDetail(dingUserId)
        val existUser = userMapper.selectOneByPhone(dingUser.mobile) ?: return
        val userCode = existUser.code!!
        // 更新钉钉扩展信息
        val updateDingUserExt = UserDingExtConvert.convert(dingUser)
        updateDingUserExt.userCode = userCode
        userDingExtMapper.updateAllFieldById(updateDingUserExt, dingUserId.toLong())
        // 更新用户信息
        val updateUser = UserConvert.convert(dingUser)
        userMapper.updateAllFieldByUserCode(updateUser, userCode, listOf(StUser::code.name, StUser::password.name, StUser::status.name, StUser::wxUnionId.name))
        // 更新部门关系
        val existDeptRelDeptCodes = userDeptRelMapper.selectListByUserCode(userCode).mapNotNull { it.departmentCode }
        val noChangeRelDeptCodes = ArrayList<String>()
        dingUser.leaderInDept.forEach {
            val existDingDeptExt = deptDingExtMapper.selectOneByDingDeptId(it.deptId)
                ?: return@forEach
            val stDeptCode = existDingDeptExt.sourceIdentifier!!
            if(existDeptRelDeptCodes.contains(existDingDeptExt.sourceIdentifier)) {
                noChangeRelDeptCodes.add(existDingDeptExt.sourceIdentifier!!)
            } else {
                userDeptRelMapper.insert( UserDeptRelConvert.convert(userCode, existDingDeptExt.sourceIdentifier!!, null))
            }
            if(it.leader) {
                val updateDept = StDepartment()
                updateDept.principalCode = userCode
                departmentMapper.updateByCode(updateDept, stDeptCode)
            }
        }
        val delRelDeptCodes = existDeptRelDeptCodes.subtract(noChangeRelDeptCodes.toSet())
        if(delRelDeptCodes.isNotEmpty()) {
            userDeptRelMapper.deleteByDeptCodes(delRelDeptCodes.toList())
        }
    }

    /**
     * 根据钉钉用户ID离职/删除一个用户信息
     */
    fun doSyncExitUserByDingUserId(dingUserId: String) {
        val userDingExt = userDingExtMapper.selectOneByUserId(dingUserId)
            ?: return
        val existUser = userMapper.selectOneByPhone(userDingExt.mobile ?: "") ?: return
        val userCode = existUser.code!!
        // 删除用户部门关系
        userDeptRelMapper.deleteByUserCode(userCode)
        // 清除是负责人的部门
        val deptList = departmentMapper.selectListByPrincipalCode(userCode)
        deptList.forEach {
            val update = StDepartment()
            update.id = it.id
            update.principalCode = ""
            departmentMapper.updateById(update)
        }
        // 删除用户角色关系
        userRoleRelMapper.deleteByUserCode(userCode)
        // 发送用户删除事件
        SpringUtil.publishEvent(UserEvent(listOf(userCode), UserEventEnum.DELETE))
    }

    /**
     * 根据钉钉部门ID删除一个部门
     */
    fun doDeleteDeptByDingDeptId(dingDeptId: Long) {
        val dingDeptExt = deptDingExtMapper.selectOneByDingDeptId(dingDeptId)
            ?: return
        val stDeptCode = dingDeptExt.sourceIdentifier ?: return
        departmentMapper.deleteByCode(stDeptCode)
        deptDingExtMapper.deleteByDeptCode(stDeptCode)
        userDeptRelMapper.deleteByDeptCode(stDeptCode)
    }

}